#ifndef CONFIGURABLE_H
#define CONFIGURABLE_H
#include <string>
#include <sstream>
#include "math/col3.h"
#include "globals.h"
namespace gemren
{
	typedef void (*property_announcer)(const char*,int,const char*); //function to announce property to the user application
	enum property_type 
	{ 
		ANNOUNCED_INT = 0, 
	  	ANNOUNCED_STRING = 1, 
		ANNOUNCED_FLOAT = 2, 
		ANNOUNCED_BOOL = 3,
		ANNOUNCED_NONE = 4
	};
	
	//interface that allows setting object's properties via string ID'd values
	class configurable
	{
	protected:
		typedef std::string string;
	public:
		virtual void announce_properties(const std::string& prefix, property_announcer announcer)
		{}
		virtual err_t set_int(std::string& name, const int value)
		{
			string atom;
			if(get_path_atom(name, atom))
			{
				configurable* c = get_conf_target(atom);
				if(c) return c->set_int(name, value);
				return ERR_BAD_CHILD;
			}
			else
				return set_my_int(atom, value);
		}
		virtual err_t set_string(std::string& name, const std::string& value)
		{
			string atom;
			if(get_path_atom(name, atom))
			{
				configurable* c = get_conf_target(atom);
				if(c) return c->set_string(name, value);
				return ERR_BAD_CHILD;
			}
			else
				return set_my_string(atom, value);
		}
		virtual err_t set_float(std::string& name, const float value)
		{
			string atom;
			if(get_path_atom(name, atom))
			{
				configurable* c = get_conf_target(atom);
				if(c) return c->set_float(name, value);
				return ERR_BAD_CHILD;
			}
			else
				return set_my_float(atom, value);
		}
		virtual err_t set_bool(std::string& name, const bool value)
		{
			string atom;
			if(get_path_atom(name, atom))
			{
				configurable* c = get_conf_target(atom);
				if(c) return c->set_bool(name, value);
				return ERR_BAD_CHILD;
			}
			else
				return set_my_bool(atom, value);
		}
		virtual err_t set_prop(std::istream& in)
		{ //eg "string rfr 1.0 1.0 1.0"
			std::string atom, value;
			in>>atom;
			if(atom == "set")
				in>>atom;
			property_type ty = str_to_type(atom);
			if(ty == ANNOUNCED_NONE)
				ty = ANNOUNCED_STRING; //default type
			else
				in>>atom;
			
			switch(ty)
			{
			case ANNOUNCED_INT:
				{
					int value = 0;
					in>>value;
					return set_int(atom, value);
					break;
				}
			case ANNOUNCED_STRING:
				{
					string value;
					in>>std::skipws;
					getline(in, value);
					value.erase(0, value.find_first_not_of(" \t"));
					return set_string(atom, value);
					break;
				}
			case ANNOUNCED_FLOAT:
				{
					float value=0;
					in>>value;
					return set_float(atom, value);
					break;
				}
			case ANNOUNCED_BOOL:
				{
					bool value = 0;
					in>>value;
					return set_bool(atom, value);
					break;
				}
			}
			return ERR_BAD_PROP;
		}
	protected:
		typedef embree::Col3f crgb;	
		
		static property_type str_to_type(const string& str)
		{
			if(str=="int") return ANNOUNCED_INT;
			if(str=="string") return ANNOUNCED_STRING;
			if(str=="float") return ANNOUNCED_FLOAT;
			if(str=="bool") return ANNOUNCED_BOOL;
			return ANNOUNCED_NONE;
		}
		
		//if applicable, breaks "obj.obj2.prop" into "obj" and "obj2.prop"
		static bool get_path_atom(std::string& path, std::string& atom)
		{
			size_t dotpos = path.find_first_of(".");
			if(dotpos==string::npos)
			{
				atom = path;
				return false;
			}
			
			atom = path.substr(0, dotpos);
			path.erase(0, dotpos+1);
			return true;
		}

		virtual configurable* get_conf_target(const std::string& name)
		{
			return 0;
		}

		virtual err_t set_my_string(std::string& name, const std::string& value)
		{
			return ERR_BAD_PROP;
		}
		virtual err_t set_my_int(std::string& name, const int value)
		{
			return ERR_BAD_PROP;
		}
		virtual err_t set_my_float(std::string& name, const float value)
		{
			return ERR_BAD_PROP;
		}
		virtual err_t set_my_bool(std::string& name, const bool value)
		{
			return ERR_BAD_PROP;
		}
		
		template<typename T> 
		void val_to_str(const T& val, string& target)
		{
			std::stringstream ss;
			ss<<val;
			target = ss.str();
		}
		
		template<typename T>
		void str_to_val(const string& src, T& dest)
		{
			std::stringstream ss(src);
			ss>>dest;
		}
		
		string bool_to_str(const bool val)
		{
			return val? "true":"false";
		}
		template<typename T> 
		string val_to_str(const T& val) const
		{
			std::stringstream ss;
			ss<<val;
			return ss.str();
		}
		string val_to_str(const embree::Vec3f& val) const
		{
			std::stringstream ss;
			ss<<val.x<<" "<<val.y<<" "<<val.z;
			return ss.str();
		}
		
		
	};
}

#endif